#####################################################################
#  Map.py
#  KnowledgeEngine
#  
#  Created by John Meredith on 2007-01-24.
#  Copyright 2007 Faulkner Technologies. All rights reserved.
#####################################################################

#####################################################################
## Knowledge Engine Library Imports
from Products.KnowledgeEngine.Common import *
from Products.KnowledgeEngine.Core import *
from Products.KnowledgeEngine.Component import Component, ComponentInfo
from Products.KnowledgeEngine.Assessible import Assessible
from Products.KnowledgeEngine.AssessmentComponentState import AssessmentComponentState
from Products.KnowledgeEngine.AssessmentComponentValueTypes import *
from Products.KnowledgeEngine.Exportable import Exportable
from Products.KnowledgeEngine.KERLExtensions import KERLExtensions
from Products.KnowledgeEngine.PropertyDefinition import *
from Products.KnowledgeEngine.PropertyTypes import *
from Products.KnowledgeEngine.PropertyFormats import *
from Products.KnowledgeEngine.PropertyStructures import *
from Products.KnowledgeEngine.KERFProcessor import *
from Products.KnowledgeEngine.KERLProcessor import InvalidKerlOperator


#####################################################################
## Zope Library Imports
from Globals import DTMLFile
from App.ImageFile import ImageFile


#####################################################################
## Python Library Imports
# (none)


FAULKNER_OFFICES = '-27.467193219240155:153.0463743209839:4';

#####################################################################
## MapInfo Class Definition
class MapInfo(ComponentInfo):
    """
        Defines the class info methods, defining information about
        the class without needing an instance of the class
    """

    #---------------------------------------------------------------#
    def getClassName(self):
        """
            Returns the classname
        """
        
        return "Map"

    
    #---------------------------------------------------------------#
    def getDisplayName(self):
        """
            Return a friendly name for the class instances (this is
            used for displaying in lists etc)
        """

        return "Map"

    
    #---------------------------------------------------------------#
    def getIdentityType(self):
        """
            Returns the identity type
        """
        
        return self.getDisplayName().lower()


    #---------------------------------------------------------------#
    def getIcon(self):
        """
            Return the appropriate icon file for the class
        """
        
        return 'Map_icon.png'


    #---------------------------------------------------------------#
    def getClassIcon(self):
        """
            Return the appropriate icon file for the class
        """
        
        return ImageFile('skins/Map_icon.png', globals())

    
    #---------------------------------------------------------------#
    def isInstanceOf(self, className):
        """
            Returns if the class is an instance of the specified
            className, or implements the interface specified 
            by the className
        """

        return className in ('Assessible', ) or ComponentInfo.isInstanceOf(self, className)

    
    #---------------------------------------------------------------#
    def onConfigureProperties(self):
        """
            Automatically called by the Knowledge Engine once
            the class has been loaded.  Use this method to register 
            class specific properties using the PropertyReflection interface.
            
            If this class is sub-classed, a call must be made to
            the super-class to ensure super-class properties are
            correctly registered
            
            NOTE: This method is called before ClassInfo.onAfterStartup()            
        """

        #initialise the super-class properties for reflection
        ComponentInfo.onConfigureProperties(self)

        self.addPropertyDefinition(PropertyDefinition(
            identity='googleMapsAPIKey',
            displayName="Google Maps API Key",
            description="A Google Maps API key is required to enable use of Google Maps",
            defaultValue="",
            propertyType=pt_STRING,
            propertyFormat=pf_TEXT_SINGLELINE,
            propertyStructure=ps_ATOMIC,
            isMandatory=true,
        ))
        
        self.addPropertyDefinition(PropertyDefinition(
            identity='mapWidth',
            displayName="Map Width",
            description="The width to display the map. Valid CSS values accepted",
            defaultValue="600px",
            propertyType=pt_STRING,
            propertyFormat=pf_TEXT_SINGLELINE,
            propertyStructure=ps_ATOMIC,
            isMandatory=true,
        ))        
        
        self.addPropertyDefinition(PropertyDefinition(
            identity='mapHeight',
            displayName="Map Height",
            description="The height to display the map. Valid CSS values accepted",
            defaultValue="400px",
            propertyType=pt_STRING,
            propertyFormat=pf_TEXT_SINGLELINE,
            propertyStructure=ps_ATOMIC,
            isMandatory=true,
        ))        

        self.addPropertyDefinition(PropertyDefinition(
            identity='defaultLocation',
            displayName="Default Location",
            description="Default location the map will center on in the form of \"latitude, longitude, zoom level\"",
            defaultValue=FAULKNER_OFFICES, # <-- Map of Australia
            propertyType=pt_STRING,
            propertyFormat=pf_TEXT_SINGLELINE,
            propertyStructure=ps_ATOMIC,
        ))

    #---------------------------------------------------------------#


#####################################################################
## Map Class Definition
class Map(Component, Assessible, KERLExtensions, Exportable):
    """
        Defines a component to visually display webbase mapping
        services in an assessment.
        
        NOTE: This component is extreme ALPHA and has been written
        as a demonstration ONLY. Not for use in production. May harm
        you and/or your children if ingested or inhaled.
    """
    
    #---------------------------------------------------------------#
    #TRANSIENT: a ClassInfo implementation instance to provide info about this class
    _CLASSINFO = MapInfo()

    
    #---------------------------------------------------------------#
    def __init__(self, id, identity):
        """
            Constructor for the Map
        """

        Component.__init__(self, id, identity)

    
    #---------------------------------------------------------------#
    def onAfterInstanciation(self):
        """
            Handle Knowledge Engine specific actions after this object 
            has been loaded into memory.  
            
            This is useful for performing backwards compatibility
        """
        
        #perform Component.onAfterInstanciation processing
        Component.onAfterInstanciation(self)

    
    #---------------------------------------------------------------#
    def onAfterIdentityChange(self, identityMappings):
        """
            Handle Knowledge Engine specific actions after the 
            identity of this object has changed.
            
            The identityMappings dictionary holds 
            mappings from oldIdentities -> newIdentities
            (including this object)
                        
        """

        #perform default identity changes
        Component.onAfterIdentityChange(self, identityMappings)


    #---------------------------------------------------------------#
    def hasReportContent(self):
        """
            This is a *hack* to overcome a new addition (r922) which
            will only display a component in the report if it has
            report content.
        """

        return True

    
    #---------------------------------------------------------------#
    # BEGIN: Generic Maps Implementation                            #
    #---------------------------------------------------------------#
    def getDefaultLocation(self):
        """
            Returns the default location given as a comma-separated
            string consisting of latitude and longitude
        """
        
        return getattr(self, "defaultLocation", FAULKNER_OFFICES)
    
    
    #---------------------------------------------------------------#
    
    def getMapWidth(self):
        """ Returns the map width """
        
        return getattr(self, 'mapWidth', '600px')
    
    #---------------------------------------------------------------#
    
    def getMapHeight(self):
        """ Returns the map height """
        
        return getattr(self, 'mapHeight', '400px')
    
    #---------------------------------------------------------------#
    
    
    #---------------------------------------------------------------#
    # END: Genric Maps Implementation                               #
    #---------------------------------------------------------------#


    #---------------------------------------------------------------#
    # BEGIN: Google Maps Implementation                             #
    #---------------------------------------------------------------#
    def getGoogleMapsAPIKey(self):
        """
            Returns the Google API key
        """
        
        return getattr(self, 'googleMapsAPIKey', '')


    #---------------------------------------------------------------#
    def getGoogleMapsJavascriptSource(self):
        """
            Returns the Google Javascript source string
            
            NOTE: Had to do it this was as I couldn't find an easy
            way of stopping tal:attributes from escaping the ampersand
        """
        
        return "<script type=\"text/javascript\" src=\"http://maps.google.com/maps?file=api&v=2&key=%s\"></script>" % self.getGoogleMapsAPIKey()
    #---------------------------------------------------------------#
    # END: Google Maps Implementation                               #
    #---------------------------------------------------------------#
    def addJavaScriptDeclarations(self, declarationsDictionary):
        """
            ABSTRACT: Adds the necessary javascript declarations to
            the declarationsDictionary which is defined as {KEY -> string}.

            The KEY is used to determine which component generated
            the declarations.  The KEY may be used to determine if
            declarations have already been generated for this type
            of component.

            The STRING is the actual javascript declaration(s).

            NOTE: When overriding, this method should always be called
            from sub-classes.
        """

        #perform the super-class additions
        Component.addJavaScriptDeclarations(self, declarationsDictionary)
        
        JAVASCRIPT_DECLARATIONS = """var mapRegistry = {};""";
            

            
        

        #add javascript declarations only if they don't already exist for this component class
        declarationsDictionary[self.getClassInfo().getClassName()] = JAVASCRIPT_DECLARATIONS
        

    #---------------------------------------------------------------#
    # BEGIN: Assessible Implementation                              #
    #---------------------------------------------------------------#
    def getAssessmentValueType(self):
        """
            ABSTRACT: Returns the type of the value that is to be stored
            for this component when it's assessment state is persisted.
            
            NOTE 1: See AssessmentComponentValueTypes for a list of defined contants       
            NOTE 2: This method MUST be overriden in the implementing
            class to specify the correct type for value persistence
            
            RETURN TYPE: String
        """
        
        #multiplechoice assessment values are strings
        return st_STRING
        
    #---------------------------------------------------------------#
        
    def getDefaultAssessmentState(self, assessment):
        """
            ABSTRACT: Constructs and returns a suitable 'default' 
            AssessmentComponentState for the provided Assessment
            instance.

            NOTE: This method MUST be overriden in the implementing
            class to provide a suitable default value for the component
            
            RETURN TYPE: AssessmentComponentState
        """
        
        return AssessmentComponentState(
            assessment.getIdentity(),
            assessment.getModelIdentity(),
            self.getIdentity(),
            self.getAssessmentValueType(),
            FAULKNER_OFFICES,
            revision=assessment.getRevision()
        )
            
    #---------------------------------------------------------------#

    def updateAssessmentState(self, assessment, formValue, skipMandatory=False):
        """
            ABSTRACT: Updates the specified assessment using
            the provided HTTP REQUEST formValue.
            
            NOTE: This method MUST be overriden in the implementing
            class to suitably update the component state
        """
        
        print "Map.updateAssessmentState formValue: %s" % (formValue, )

        Component.updateAssessmentState( self, assessment, formValue, skipMandatory=skipMandatory )
        
        #get the current assessment state for the component from the assessment
        state = assessment.getComponentState(self.getIdentity())
        
        #update the current state
        state.setValue(formValue)
        
        #update the assessment
        assessment.setIsModified(true)
    #---------------------------------------------------------------#
    # END: Assessible Implementation                                #
    #---------------------------------------------------------------#
    

#####################################################################
# Register Component Information with the Knowledge Engine
registerGenericObjectClass(Map)
